package services

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	"time"
)

var DBEngine *gorm.DB

func init() {

	db := ConfigServiceInstance.Database
	driverName, userName, password, address, port, tableName := db.Type, db.UserName, db.Password, db.Address, db.Port, db.TableName
	if len(driverName) == 0 {
		driverName = "mysql"
	}
	newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
		SlowThreshold:             time.Second,
		LogLevel:                  logger.Info,
		IgnoreRecordNotFoundError: true,
		Colorful:                  true,
	})
	open, err := gorm.Open(mysql.Open(fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", userName, password, address, port, tableName)), &gorm.Config{QueryFields: true, Logger: newLogger})
	if err != nil {
		return
	}
	DBEngine = open
}
func AbortWithError(c *gin.Context, err error) {
	code := http.StatusInternalServerError
	c.AbortWithStatusJSON(code, gin.H{
		"error": err.Error(),
	})
	return
}
func AbortWithDatabaseError(c *gin.Context, err error) {
	code := http.StatusInternalServerError
	if errors.Is(err, gorm.ErrRecordNotFound) {
		code = http.StatusNotFound
	}
	c.AbortWithStatusJSON(code, gin.H{
		"error": err.Error(),
	})
	return
}
func escapeKeyName(keyName string) string {
	replace := map[string]string{"\\":"\\\\", "'":`\'`, "\\0":"\\\\0","\n":"\\n","\t":"\\t",`"`:`\"`,"\x1a":"\\Z"}
	for rK,rV := range replace {
		keyName = strings.ReplaceAll(keyName,rK,rV)
	}
	return url.QueryEscape(keyName)
}
func advanceWhereBuild(targetKey string, where interface{}, db *gorm.DB) *gorm.DB {
	mapValue := reflect.ValueOf(where)
	if mapValue.Kind() != reflect.Map {
		return db
	}
	mapIter := mapValue.MapRange()
	for mapIter.Next() {
		key := mapIter.Key()
		value := mapIter.Value()
		if key.Kind() != reflect.String {
			continue
		}
		switch key.String() {
		case "$like":
			db = db.Where(fmt.Sprintf("%s like ?", escapeKeyName(targetKey)), value.Interface())
		case "$not":
			db = db.Where(fmt.Sprintf("%s not ?", escapeKeyName(targetKey)), value.Interface())
		}
	}
	return db
}
func BuildWhere(where map[string]interface{}, db *gorm.DB) *gorm.DB {
	for key, value := range where {
		reflectValue := reflect.ValueOf(value)
		if reflectValue.Kind() == reflect.Ptr || reflectValue.Kind() == reflect.Interface {
			reflectValue = reflectValue.Elem()
		}
		switch reflectValue.Kind() {
		case reflect.Map:
			db = advanceWhereBuild(key, value, db)
		default:
			db = db.Where(fmt.Sprintf("%s = ?", escapeKeyName(key)), value)
		}

	}
	return db
}
